/*
http://github.com/danpalmer/jquery.complexify.js

This code is distributed under the WTFPL v2:
 */
(function($) {

	$.fn.extend( {
		complexify : function(options, callback) {

			var MIN_COMPLEXITY = 49; // 12 chars with Upper, Lower and Number
			var MAX_COMPLEXITY = 120; // 25 chars, all charsets
			var CHARSETS = [
			// Commonly Used
					// //////////////////
					[ 0x0030, 0x0039 ], // Numbers
					[ 0x0041, 0x005A ], // Uppercase
					[ 0x0061, 0x007A ], // Lowercase
					[ 0x0021, 0x002F ], // Punctuation
					[ 0x003A, 0x0040 ], // Punctuation
					[ 0x005B, 0x0060 ], // Punctuation
					[ 0x007B, 0x007E ], // Punctuation
					// Everything Else
					// //////////////////
					[ 0x0080, 0x00FF ], // Latin-1 Supplement
					[ 0x0100, 0x017F ], // Latin Extended-A
					[ 0x0180, 0x024F ], // Latin Extended-B
					[ 0x0250, 0x02AF ], // IPA Extensions
					[ 0x02B0, 0x02FF ], // Spacing Modifier Letters
					[ 0x0300, 0x036F ], // Combining Diacritical Marks
					[ 0x0370, 0x03FF ], // Greek
					[ 0x0400, 0x04FF ], // Cyrillic
					[ 0x0530, 0x058F ], // Armenian
					[ 0x0590, 0x05FF ], // Hebrew
					[ 0x0600, 0x06FF ], // Arabic
					[ 0x0700, 0x074F ], // Syriac
					[ 0x0780, 0x07BF ], // Thaana
					[ 0x0900, 0x097F ], // Devanagari
					[ 0x0980, 0x09FF ], // Bengali
					[ 0x0A00, 0x0A7F ], // Gurmukhi
					[ 0x0A80, 0x0AFF ], // Gujarati
					[ 0x0B00, 0x0B7F ], // Oriya
					[ 0x0B80, 0x0BFF ], // Tamil
					[ 0x0C00, 0x0C7F ], // Telugu
					[ 0x0C80, 0x0CFF ], // Kannada
					[ 0x0D00, 0x0D7F ], // Malayalam
					[ 0x0D80, 0x0DFF ], // Sinhala
					[ 0x0E00, 0x0E7F ], // Thai
					[ 0x0E80, 0x0EFF ], // Lao
					[ 0x0F00, 0x0FFF ], // Tibetan
					[ 0x1000, 0x109F ], // Myanmar
					[ 0x10A0, 0x10FF ], // Georgian
					[ 0x1100, 0x11FF ], // Hangul Jamo
					[ 0x1200, 0x137F ], // Ethiopic
					[ 0x13A0, 0x13FF ], // Cherokee
					[ 0x1400, 0x167F ], // Unified Canadian Aboriginal Syllabics
					[ 0x1680, 0x169F ], // Ogham
					[ 0x16A0, 0x16FF ], // Runic
					[ 0x1780, 0x17FF ], // Khmer
					[ 0x1800, 0x18AF ], // Mongolian
					[ 0x1E00, 0x1EFF ], // Latin Extended Additional
					[ 0x1F00, 0x1FFF ], // Greek Extended
					[ 0x2000, 0x206F ], // General Punctuation
					[ 0x2070, 0x209F ], // Superscripts and Subscripts
					[ 0x20A0, 0x20CF ], // Currency Symbols
					[ 0x20D0, 0x20FF ], // Combining Marks for Symbols
					[ 0x2100, 0x214F ], // Letterlike Symbols
					[ 0x2150, 0x218F ], // Number Forms
					[ 0x2190, 0x21FF ], // Arrows
					[ 0x2200, 0x22FF ], // Mathematical Operators
					[ 0x2300, 0x23FF ], // Miscellaneous Technical
					[ 0x2400, 0x243F ], // Control Pictures
					[ 0x2440, 0x245F ], // Optical Character Recognition
					[ 0x2460, 0x24FF ], // Enclosed Alphanumerics
					[ 0x2500, 0x257F ], // Box Drawing
					[ 0x2580, 0x259F ], // Block Elements
					[ 0x25A0, 0x25FF ], // Geometric Shapes
					[ 0x2600, 0x26FF ], // Miscellaneous Symbols
					[ 0x2700, 0x27BF ], // Dingbats
					[ 0x2800, 0x28FF ], // Braille Patterns
					[ 0x2E80, 0x2EFF ], // CJK Radicals Supplement
					[ 0x2F00, 0x2FDF ], // Kangxi Radicals
					[ 0x2FF0, 0x2FFF ], // Ideographic Description Characters
					[ 0x3000, 0x303F ], // CJK Symbols and Punctuation
					[ 0x3040, 0x309F ], // Hiragana
					[ 0x30A0, 0x30FF ], // Katakana
					[ 0x3100, 0x312F ], // Bopomofo
					[ 0x3130, 0x318F ], // Hangul Compatibility Jamo
					[ 0x3190, 0x319F ], // Kanbun
					[ 0x31A0, 0x31BF ], // Bopomofo Extended
					[ 0x3200, 0x32FF ], // Enclosed CJK Letters and Months
					[ 0x3300, 0x33FF ], // CJK Compatibility
					[ 0x3400, 0x4DB5 ], // CJK Unified Ideographs Extension A
					[ 0x4E00, 0x9FFF ], // CJK Unified Ideographs
					[ 0xA000, 0xA48F ], // Yi Syllables
					[ 0xA490, 0xA4CF ], // Yi Radicals
					[ 0xAC00, 0xD7A3 ], // Hangul Syllables
					[ 0xD800, 0xDB7F ], // High Surrogates
					[ 0xDB80, 0xDBFF ], // High Private Use Surrogates
					[ 0xDC00, 0xDFFF ], // Low Surrogates
					[ 0xE000, 0xF8FF ], // Private Use
					[ 0xF900, 0xFAFF ], // CJK Compatibility Ideographs
					[ 0xFB00, 0xFB4F ], // Alphabetic Presentation Forms
					[ 0xFB50, 0xFDFF ], // Arabic Presentation Forms-A
					[ 0xFE20, 0xFE2F ], // Combining Half Marks
					[ 0xFE30, 0xFE4F ], // CJK Compatibility Forms
					[ 0xFE50, 0xFE6F ], // Small Form Variants
					[ 0xFE70, 0xFEFE ], // Arabic Presentation Forms-B
					[ 0xFEFF, 0xFEFF ], // Specials
					[ 0xFF00, 0xFFEF ], // Halfwidth and Fullwidth Forms
					[ 0xFFF0, 0xFFFD ] // Specials
			];

			var defaults = {
				minimumChars : 8,
				strengthScaleFactor : 1,
				bannedPasswords : [],
				banmode : 'strict' // (strict|loose)
			};
			if ($.isFunction(options) && !callback) {
				callback = options;
				options = {};
			}
			options = $.extend(defaults, options);

			function additionalComplexityForCharset(str, charset) {
				for ( var i = str.length - 1; i >= 0; i--) {
					if (charset[0] <= str.charCodeAt(i)
							&& str.charCodeAt(i) <= charset[1]) {
						return charset[1] - charset[0] + 1;
					}
					;
				}
				;
				return 0;
			}
			;

			function inBanlist(str) {
				if (options.banmode === 'strict') {
					for ( var i = 1; i <= str.length; i++) {
						if ($
								.inArray(str.substr(0, i),
										options.bannedPasswords) > -1) {
							// Will return true if a word from the list appears
							// at
							// the beginning of the password
							return true;
						}
					}
					return false;
				} else {
					return $.inArray(str, options.bannedPasswords) > -1 ? true
							: false;
				}
			}

			return this
					.each(function() {
						$(this)
								.keyup(function() {
									var password = $(this).val();
									var complexity = 0, valid = false;

									// Reset complexity to 0 when banned password is found
										if (!inBanlist(password)) {

											// Add character complexity
											for ( var i = CHARSETS.length - 1; i >= 0; i--) {
												complexity += additionalComplexityForCharset(
														password, CHARSETS[i]);
											}

										} else {
											complexity = 1;
										}

										// Use natural log to produce linear scale
										complexity = Math.log(Math.pow(
												complexity, password.length))
												* (1 / options.strengthScaleFactor);

										valid = (complexity > MIN_COMPLEXITY && password.length >= options.minimumChars);

										// Scale to percentage, so it can be used for a progress bar
										complexity = (complexity / MAX_COMPLEXITY) * 100;
										complexity = (complexity > 100) ? 100
												: complexity;

										callback.call(this, valid, complexity);
									});
					});

		}
	});

})(jQuery);